#include "stdafx.h"
#include <vector>


static char32_t Add32(char32_t x) { return x + 32; };
static char32_t Subtract32(char32_t x) { return x - 32; };
static char32_t Xor1(char32_t x) { return x ^ 1; };
static char32_t IncrementedXor1(char32_t x) { return ((x + 1) ^ 1) - 1; };
static char32_t AddX50(char32_t x) { return x + 0x50; };
static char32_t SubtractX50(char32_t x) { return x - 0x50; };
static char32_t AddX30(char32_t x) { return x + 0x30; };
static char32_t SubtractX30(char32_t x) { return x - 0x30; };
static char32_t Xor8(char32_t x) { return x ^ 8; };


static char32_t ConvertLatinExtendedB(char32_t x)
{
	static const signed char kDelta182[] =
	{
		1, -1, 1, -1, 0, 1, -1, 0, 0, 1, -1, 0, 0, 0,  // 182..18f
		0, 1, -1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, // 190-19f
		1, -1, 1, -1, 1, -1, 0, 1, -1, 0, 0, 0, 1, -1, 0, 1, // 1a0-1af
		-1, 0, 0, 1, -1, 1, -1, 0, 1, -1, 0, 0, 1, -1, 0, 0, // 1b0-1bf
		0, 0, 0, 0, 2, 1, -2, 2, 1, -2, 2, 1, -2, 1, -1, 1, // 1c0-1cf
		-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 0, 1, -1, // 1d0-1df
		1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, // 1e0-1ef
		0, 2, 1, -2, 1, -1 // 1f0-1f5
	};
	return (x + kDelta182[x - 0x182]);
}

static char32_t ConvertLatinExtendedB_1(char32_t x)
{
	static const signed char kDelta23b[] =
	{
		1, -1, 0, 0, 0, // 23b-23f
		0, 1, -1, 0, 0, 0,  1, -1, 1, -1, 1, -1, 1, -1, 1, -1 // 240-24f
	};
	return (x + kDelta23b[x - 0x23b]);
}

typedef char32_t (*ConversionFunc)(char32_t);
struct RangeInfo { char32_t rangeBegin, rangeEnd; ConversionFunc f; };

// the elements of the kRangeInfo are ordered
static const RangeInfo kRangeInfos[] = 
{
	{ 0x41, 0x5a, &Add32 },			// latin capital
	{ 0x61, 0x7a, &Subtract32 },	// latin small
	{ 0xc0, 0xd6, &Add32 },			// latin capital with diacritics
	{ 0xd8, 0xde, &Add32 },			// latin capital with diacritics
	{ 0xe0, 0xf6, &Subtract32 },	// latin small with diacritics
	{ 0xf8, 0xfe, &Subtract32 },	// latin small with diacritics
	{ 0x100, 0x12f, &Xor1 },		// latin extended-A, mixed case
	{ 0x132, 0x137, &Xor1 },		// latin extended-A, mixed case
	{ 0x139, 0x148, &IncrementedXor1 },	// latin extended-A, mixed case
	{ 0x14a, 0x17e, &Xor1 },		// latin extended-A, mixed case

	{ 0x182, 0x1f5, &ConvertLatinExtendedB },		// latin extended-B, mixed case
	{ 0x1f8, 0x21f, &Xor1 },
	{ 0x222, 0x233, &Xor1 },
	{ 0x23b, 0x24f, &ConvertLatinExtendedB_1 },		// latin extended-B, mixed case

	{ 0x391, 0x3a1, &Add32 },		// greek capital
	{ 0x3a3, 0x3ab, &Add32 },		// greek capital
	{ 0x3b1, 0x3c1, &Subtract32 },	// greek small
	{ 0x3c3, 0x3cb, Subtract32 },	// greek small
	{ 0x3d8, 0x3ef, &Xor1 },		// greek and coptic

	{ 0x400, 0x40f, &AddX50 },		// cyrillic capital various
	{ 0x410, 0x42f, &Add32 },		// cyrillic capital
	{ 0x430, 0x44f, &Subtract32 },	// cyrillic small
	{ 0x450, 0x45f, &SubtractX50 },	// cyrillic small various
	{ 0x460, 0x481, &Xor1 },		// cyrillic mixed case
	{ 0x48a, 0x4bf, &Xor1 },		// cyrillic mixed case
	{ 0x4c1, 0x4ce, &IncrementedXor1 }, // cyrillic mixed case
	{ 0x4d0, 0x4ff, &Xor1 },		// cyrillic mixed case
	{ 0x500, 0x52f, &Xor1 },		// cyrillic supplement mixed case

	{ 0x531, 0x556, &AddX30 },		// armenian capital
	{ 0x561, 0x586, &SubtractX30 },	// armenian small

	{ 0x10a0, 0x10c5, &AddX30 },	// georgian capital
	{ 0x10d0, 0x10f5, &SubtractX30 }, // georgian small

	{ 0x1e00, 0x1e95, &Xor1 },		// latin extended additional
	{ 0x1ea0, 0x1eff, &Xor1 },		// latin extended additional
	{ 0x1f00, 0x1f15, &Xor8 },		// greek extended
	{ 0x1f18, 0x1f1d, &Xor8 },		// greek extended

	// 2800
};

static const char32_t kSmallSharpS = 0x00df;
static const char32_t kCapitalSharpS = 0x1e9e;

char32_t ToggleCase(char32_t inChar)
{
	char32_t result = inChar;

	// binary search
	int a = 0, b = sizeof(kRangeInfos) / sizeof(kRangeInfos[0]);
	while (a < b)
	{
		int i = (a + b) / 2;
		if (kRangeInfos[i].rangeBegin <= inChar)
		{
			a = i + 1;
		}
		else
		{
			b = i;
		}
	}
	if (0 < a)
	{
		auto& info = kRangeInfos[a - 1];
		if (inChar <= info.rangeEnd)
		{
			result = info.f(inChar);
		}

		// special case for German letter Sharp S.
		else if (kSmallSharpS == inChar)
		{
			result = kCapitalSharpS;
		}
		else if (kCapitalSharpS == inChar)
		{
			result = kSmallSharpS;
		}
	}

	return result;
}